#include "glm_bis.h"

static void glmFirstPass(Mmodel* model, FILE * file) 
{
	unsigned int numvertices;        /* number of vertices in model */
	unsigned int numnormals;         /* number of normals in model */
	unsigned int numtexcoords;       /* number of texcoords in model */
	unsigned int numtriangles;       /* number of triangles in model */
	Mgroup* group;           /* current group */
	int v, n, t;
	char buf[128];
	/* make a default group */
	group = glmAddGroup(model, "default");
	numvertices = numnormals = numtexcoords = numtriangles = 0;
	while(fscanf(file, "%s", buf) != EOF)
	{
		switch(buf[0])
		{
			case '#':               /* comment */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'v':               /* v, vn, vt */
				switch(buf[1])
				{
					case '\0':          /* vertex */
						/* eat up rest of line */
						fgets(buf, sizeof(buf), file);
						numvertices++;
						break;
					case 'n':           /* normal */
						/* eat up rest of line */
						fgets(buf, sizeof(buf), file);
						numnormals++;
						break;
					case 't':           /* texcoord */
						/* eat up rest of line */
						fgets(buf, sizeof(buf), file);
						numtexcoords++;
						break;
					default:
						printf("glmFirstPass(): Unknown token \"%s\".\n", buf);
						fclose(file);
						exit(1);
						break;
				}
				break;
			case 'm':
				fgets(buf, sizeof(buf), file);
				sscanf(buf, "%s %s", buf, buf);
				model->mtllibname = strdup(buf);
				glmReadMTL(model, buf);
				break;
			case 'u':
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'g':               /* group */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				buf[strlen(buf)-1] = '\0';  /* nuke '\n' */
				group = glmAddGroup(model, buf);
				break;
			case 'f':               /* face */
				v = n = t = 0;
				fscanf(file, "%s", buf);
				/* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
				if (strstr(buf, "//"))
				{
					/* v//n */
					sscanf(buf, "%d//%d", &v, &n);
					fscanf(file, "%d//%d", &v, &n);
					fscanf(file, "%d//%d", &v, &n);
					numtriangles++;
					group->numtriangles++;
					while(fscanf(file, "%d//%d", &v, &n) > 0)
					{
						numtriangles++;
						group->numtriangles++;
					}
				}
				else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3)
				{
					/* v/t/n */
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					numtriangles++;
					group->numtriangles++;
					while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0)
					{
						numtriangles++;
						group->numtriangles++;
					}
				}
				else if (sscanf(buf, "%d/%d", &v, &t) == 2)
				{
					/* v/t */
					fscanf(file, "%d/%d", &v, &t);
					fscanf(file, "%d/%d", &v, &t);
					numtriangles++;
					group->numtriangles++;
					while(fscanf(file, "%d/%d", &v, &t) > 0)
					{
						numtriangles++;
						group->numtriangles++;
					}
				}
				else
				{
					/* v */
					fscanf(file, "%d", &v);
					fscanf(file, "%d", &v);
					numtriangles++;
					group->numtriangles++;
					while(fscanf(file, "%d", &v) > 0)
					{
						numtriangles++;
						group->numtriangles++;
					}
				}
				break;
			default:
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
		}
	}
	/* set the stats in the model structure */
	model->numvertices  = numvertices;
	model->numnormals   = numnormals;
	model->numtexcoords = numtexcoords;
	model->numtriangles = numtriangles;
	/* allocate memory for the triangles in each group */
	group = model->groups;
	while(group)
	{
		group->triangles = new unsigned int[group->numtriangles];//(unsigned int*)malloc(sizeof(unsigned int) * group->numtriangles);
		group->numtriangles = 0;
		group = group->next;
	}
}

static void glmSecondPass(Mmodel* model, FILE* file) 
{
	unsigned int numvertices;        /* number of vertices in model */
	unsigned int numnormals;         /* number of normals in model */
	unsigned int numtexcoords;       /* number of texcoords in model */
	unsigned int numtriangles;       /* number of triangles in model */
	float* vertices;         /* array of vertices  */
	float* normals;          /* array of normals */
	float* texcoords;        /* array of texture coordinates */
	Mgroup* group;           /* current group pointer */
	unsigned int material;           /* current material */
	int v, n, t;
	char buf[128];
	/* set the pointer shortcuts */
	vertices       = model->vertices;
	normals    = model->normals;
	texcoords    = model->texcoords;
	group      = model->groups;
	/* on the second pass through the file, read all the data into the
	allocated arrays */
	numvertices = numnormals = numtexcoords = 1;
	numtriangles = 0;
	material = 0;
	while(fscanf(file, "%s", buf) != EOF)
	{
		switch(buf[0])
		{
			case '#':               /* comment */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'v':               /* v, vn, vt */
				switch(buf[1])
				{
				case '\0':          /* vertex */
					fscanf(file, "%f %f %f", 
					&vertices[3 * numvertices + 0], 
					&vertices[3 * numvertices + 1], 
					&vertices[3 * numvertices + 2]);
					numvertices++;
					break;
				case 'n':           /* normal */
					fscanf(file, "%f %f %f", 
					&normals[3 * numnormals + 0],
					&normals[3 * numnormals + 1], 
					&normals[3 * numnormals + 2]);
					numnormals++;
					break;
				case 't':           /* texcoord */
					fscanf(file, "%f %f", 
					&texcoords[2 * numtexcoords + 0],
					&texcoords[2 * numtexcoords + 1]);
					numtexcoords++;
					break;
				}
				break;
			case 'u':
				fgets(buf, sizeof(buf), file);
				sscanf(buf, "%s %s", buf, buf);
				group->material = material = glmFindMaterial(model, buf);
				break;
			case 'g':               /* group */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				buf[strlen(buf)-1] = '\0';  /* nuke '\n' */
				group = glmFindGroup(model, buf);
				group->material = material;
				break;
			case 'f':               /* face */
				v = n = t = 0;
				fscanf(file, "%s", buf);
				/* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
				if (strstr(buf, "//"))
				{
					/* v//n */
					sscanf(buf, "%d//%d", &v, &n);
					T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
					T(numtriangles).nindices[0] = n < 0 ? n + numnormals : n;
					fscanf(file, "%d//%d", &v, &n);
					T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
					T(numtriangles).nindices[1] = n < 0 ? n + numnormals : n;
					fscanf(file, "%d//%d", &v, &n);
					T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
					T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
					group->triangles[group->numtriangles++] = numtriangles;
					numtriangles++;
					while(fscanf(file, "%d//%d", &v, &n) > 0)
					{
						T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
						T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
						T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
						T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
						T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
						T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
						group->triangles[group->numtriangles++] = numtriangles;
						numtriangles++;
					}
				}
				else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3)
				{
					/* v/t/n */
					T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
					T(numtriangles).tindices[0] = t < 0 ? t + numtexcoords : t;
					T(numtriangles).nindices[0] = n < 0 ? n + numnormals : n;
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
					T(numtriangles).tindices[1] = t < 0 ? t + numtexcoords : t;
					T(numtriangles).nindices[1] = n < 0 ? n + numnormals : n;
					fscanf(file, "%d/%d/%d", &v, &t, &n);
					T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
					T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
					T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
					group->triangles[group->numtriangles++] = numtriangles;
					numtriangles++;
					while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0)
					{
						T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
						T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
						T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
						T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
						T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
						T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
						T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
						T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
						T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
						group->triangles[group->numtriangles++] = numtriangles;
						numtriangles++;
					}
				}
				else if (sscanf(buf, "%d/%d", &v, &t) == 2)
				{
					/* v/t */
					T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
					T(numtriangles).tindices[0] = t < 0 ? t + numtexcoords : t;
					fscanf(file, "%d/%d", &v, &t);
					T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
					T(numtriangles).tindices[1] = t < 0 ? t + numtexcoords : t;
					fscanf(file, "%d/%d", &v, &t);
					T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
					T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
					group->triangles[group->numtriangles++] = numtriangles;
					numtriangles++;
					while(fscanf(file, "%d/%d", &v, &t) > 0)
					{
						T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
						T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
						T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
						T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
						T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
						T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
						group->triangles[group->numtriangles++] = numtriangles;
						numtriangles++;
					}
				}
				else
				{
					/* v */
					sscanf(buf, "%d", &v);
					T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
					fscanf(file, "%d", &v);
					T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
					fscanf(file, "%d", &v);
					T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
					group->triangles[group->numtriangles++] = numtriangles;
					numtriangles++;
					while(fscanf(file, "%d", &v) > 0)
					{
						T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
						T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
						T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
						group->triangles[group->numtriangles++] = numtriangles;
						numtriangles++;
					}
				}
				break;
			default:
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
		}
	}
}

Mgroup* glmFindGroup(Mmodel* model, char* name)
{
	Mgroup* group;
	assert(model);
	group = model->groups;
	while(group)
	{
		if (!strcmp(name, group->name))
		break;
		group = group->next;
	}
	return group;
}

Mgroup* glmAddGroup(Mmodel* model, char* name)
{
	Mgroup* group;
	group = glmFindGroup(model, name);
	if (!group)
	{
		group = new Mgroup();
		group->name = strdup(name);
		group->material = 0;
		group->numtriangles = 0;
		group->triangles = NULL;
		group->next = model->groups;
		model->groups = group;
		model->numgroups++;
	}
	return group;
}

Mmodel* glmReadOBJ(char* filename)
{
	Mmodel* model;
	FILE* file;
	/* open the file */
	file = fopen(filename, "r");
	if (!file)
	{
		fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
		filename);
		exit(1);
	}
	/* allocate a new model */
	model = new Mmodel();
	model->pathname    = strdup(filename);
	model->mtllibname    = NULL;
	model->numvertices   = 0;
	model->vertices    = NULL;
	model->numnormals    = 0;
	model->normals     = NULL;
	model->numtexcoords  = 0;
	model->texcoords       = NULL;
	model->numtriangles  = 0;
	model->triangles       = NULL;
	model->nummaterials  = 0;
	model->materials       = NULL;
	model->numgroups       = 0;
	model->groups      = NULL;
	model->position[0]   = 0.0;
	model->position[1]   = 0.0;
	model->position[2]   = 0.0;
	/* make a first pass through the file to get a count of the number
	of vertices, normals, texcoords & triangles */
	glmFirstPass(model, file);
	/* allocate memory */
	model->vertices = new float[3 * (model->numvertices + 1)];
	model->triangles = new Mtriangle[model->numtriangles];
	if (model->numnormals)
	{
		model->normals = new float[3 * (model->numnormals + 1)];
	}
	if (model->numtexcoords)
	{
		model->texcoords = new float[2 * (model->numtexcoords + 1)];
	}
	/* rewind to beginning of file and read in the data this pass */
	rewind(file);
	glmSecondPass(model, file);
	/* close the file */
	fclose(file);
	return model;
}

static void glmReadMTL(Mmodel* model, char* name)
{
	FILE* file;
	char* dir;
	char* filename;
	char buf[128];
	unsigned int nummaterials, i;
	dir = glmDirName(model->pathname);
	filename = new char[strlen(dir) + strlen(name) + 1];
	strcpy(filename, dir);
	strcat(filename, name);
	free(dir);
	file = fopen(filename, "r");
	if (!file)
	{
		fprintf(stderr, "glmReadMTL() failed: can't open material file \"%s\".\n",
		filename);
		exit(1);
	}
	free(filename);
	/* count the number of materials in the file */
	nummaterials = 1;
	while(fscanf(file, "%s", buf) != EOF)
	{
		switch(buf[0])
		{
			case '#':               /* comment */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'n':               /* newmtl */
				fgets(buf, sizeof(buf), file);
				nummaterials++;
				sscanf(buf, "%s %s", buf, buf);
				break;
			default:
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
		}
	}
	rewind(file);
	model->materials = new Mmaterial[nummaterials];
	model->nummaterials = nummaterials;
	/* set the default material */
	for (i = 0; i < nummaterials; i++) {
		model->materials[i].name = NULL;
		model->materials[i].shininess = 65.0;
		model->materials[i].diffuse[0] = 0.8;
		model->materials[i].diffuse[1] = 0.8;
		model->materials[i].diffuse[2] = 0.8;
		model->materials[i].diffuse[3] = 1.0;
		model->materials[i].ambient[0] = 0.2;
		model->materials[i].ambient[1] = 0.2;
		model->materials[i].ambient[2] = 0.2;
		model->materials[i].ambient[3] = 1.0;
		model->materials[i].specular[0] = 0.0;
		model->materials[i].specular[1] = 0.0;
		model->materials[i].specular[2] = 0.0;
		model->materials[i].specular[3] = 1.0;
	}
	model->materials[0].name = strdup("default");
	/* now, read in the data */
	nummaterials = 0;
	while(fscanf(file, "%s", buf) != EOF)
	{
		switch(buf[0])
		{
			case '#':               /* comment */
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
			case 'n':               /* newmtl */
				fgets(buf, sizeof(buf), file);
				sscanf(buf, "%s %s", buf, buf);
				nummaterials++;
				model->materials[nummaterials].name = strdup(buf);
				break;
			case 'N':
				fscanf(file, "%f", &model->materials[nummaterials].shininess);
				/* wavefront shininess is from [0, 1000], so scale for OpenGL */
				model->materials[nummaterials].shininess /= 1000.0;
				model->materials[nummaterials].shininess *= 128.0;
				break;
			case 'K':
				switch(buf[1])
				{
					case 'd':
						fscanf(file, "%f %f %f",
						&model->materials[nummaterials].diffuse[0],
						&model->materials[nummaterials].diffuse[1],
						&model->materials[nummaterials].diffuse[2]);
						break;
					case 's':
						fscanf(file, "%f %f %f",
						&model->materials[nummaterials].specular[0],
						&model->materials[nummaterials].specular[1],
						&model->materials[nummaterials].specular[2]);
						break;
					case 'a':
						fscanf(file, "%f %f %f",
						&model->materials[nummaterials].ambient[0],
						&model->materials[nummaterials].ambient[1],
						&model->materials[nummaterials].ambient[2]);
						break;
					default:
						/* eat up rest of line */
						fgets(buf, sizeof(buf), file);
						break;
				}
				break;
			default:
				/* eat up rest of line */
				fgets(buf, sizeof(buf), file);
				break;
		}
	}
}

static char* glmDirName(char* path)
{
	char* dir;
	char* s;
	dir = strdup(path);
	s = strrchr(dir, '/');
	if (s)
		s[1] = '\0';
	else
		dir[0] = '\0';
	return dir;
}

unsigned int glmFindMaterial(Mmodel* model, char* name)
{
	unsigned int i;
	/* XXX doing a linear search on a string key'd list is pretty lame,
	but it works and is fast enough for now. */
	for (i = 0; i < model->nummaterials; i++) {
		if (!strcmp(model->materials[i].name, name))
		goto found;
	}
	/* didn't find the name, so print a warning and return the default
	material (0). */
	printf("glmFindMaterial():  can't find material \"%s\".\n", name);
	i = 0;
	found:
	return i;
}
